<template>
	<div class="map-box">
		<div ref="map" class="map"></div>
		<div ref="tooltip" class="tooltip"></div>
	</div>
</template>

<script>
import * as THREE from 'three';
import { Detector } from '@/utils/detector';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import gsap from 'gsap'; // 导入动画库
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import * as d3 from 'd3-geo';

let camera = null; // 相机
let scene = null; // 场景
let renderer = null; // 渲染
// let light = null; // 灯光
// let raycaster = null; // 光线投射
let mouse = null; // mouse
let controls = null;
// let state = new State(); // 帧率检测
let width = 0;
let height = 0;
// let cube = null;
// let geometry = null;
let material = null;
let raycaster = null; // 鼠标移入监听
// 初始化一个地图对象
const lines = [];
let indexBol = true;
let currentPos = 0;
let pointSpeed = 10; // 速度
let points = null; // 点数据
const geometry = new THREE.BufferGeometry();
let positions = null; // 位置
let opacitys = null; // 透明度
let s = 8; //三维场景显示范围控制系数，系数越大，显示的范围越大，看到的东西越小
// 墨卡托投影转换
const projection = d3.geoMercator().center([104.065735, 30.659462]).translate([0, 0]).scale(90);

export default {
	name: 'Index',
	data() {
		return {
			tooltip: '',
			lastPick: null
		};
	},
	mounted() {
		this.init();
	},
	methods: {
		// 换色
		click(color) {
			material.color.set(color);
		},
		init() {
			if (!Detector.webgl) return Detector.addGetWebGLMessage(), alert('不支持webgl');
			width = this.$refs.map.offsetWidth; // 获取显示宽度和高度
			height = this.$refs.map.offsetHeight;
			scene = new THREE.Scene(); // 建立场景
			// 坐标轴 辅助
			// let axisHelper = new THREE.AxesHelper(250);
			// scene.add(axisHelper);
			this.initScene();
			this.initModel();
			this.initRenderer();
			// this.initHdr();
			this.initControls();
			this.setRaycaster();
			this.loadMapData(); // 加载地图数据
			this.initBorder(); // 加载地图边框数据
			this.initPlane();
			this.makeBox();
			// this.animation();
			this.render();
		},
		// 创建柱子
		makeBox() {
			let depth = 5;
			const geometry = new THREE.BoxGeometry(0.3, 0.3, depth);
			const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
			const cube = new THREE.Mesh(geometry, material);
			cube.position.set(-5, -1, -depth / 2);
			scene.add(cube);
			gsap.to([cube.position], {
				duration: 3,
				z: depth / 2,
				// repeat: -1, // 重复次数 无限循环-1
				// yoyo: true, // 往返运动
				// delay: 2, // 延迟时间
				ease: 'power1.inOut' // 动画类型
			});
		},

		// 加载地图数据
		loadMapData() {
			// FileLoader: 用于加载任何没有对应加载器的文件类型。
			const loader = new THREE.FileLoader();
			loader.load('json/sichuan.json', data => {
				const jsonData = JSON.parse(data);
				this.initMap(jsonData);
			});
		},
		// 地图渲染
		initMap(jsonData) {
			// 初始化一个地图对象
			let map = new THREE.Object3D();

			let textureLoader = new THREE.TextureLoader(); // 纹理加载器
			let texture = textureLoader.load(require('@/assets/img/1.jpeg'));
			texture.wrapS = texture.wrapT = THREE.RepeatWrapping; // CHANGED
			// texture.offset.set( 0, 0 ); // CHANGED
			texture.repeat.set(0.1, 0.1); // CHANGED
			console.log(texture);
			jsonData.features.forEach(elem => {
				// 定一个省份3D对象
				const province = new THREE.Object3D();

				// 每个的 坐标 数组
				const coordinates = elem.geometry.coordinates;
				console.log(elem.properties.name);
				let name = elem.properties.name;

				coordinates.forEach(multiPolygon => {
					multiPolygon.forEach(polygon => {
						// shape：使用路径以及可选的孔洞来定义一个二维形状平面
						const shape = new THREE.Shape();
						// 线条的材质
						const lineMaterial = new THREE.LineBasicMaterial({
							color: '#fff',
							transparent: true,
							opacity: 1
						});
						// BufferGeometry: 是面片、线或点几何体的有效表述。包括顶点位置，面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。
						const lineGeometry = new THREE.BufferGeometry();
						let arrNew = [];
						// 画线和形成平面
						for (let i = 0; i < polygon.length; i++) {
							const [x, y] = projection(polygon[i]);
							if (i === 0) {
								shape.moveTo(x, -y);
							}
							shape.lineTo(x, -y);
							arrNew.push(new THREE.Vector3(x, -y, 1.01));
						}
						// setFromPoints: 通过点队列设置该 BufferGeometry 的 attribute。
						lineGeometry.setFromPoints(arrNew);

						// 定义深度
						const extrudeSettings = {
							depth: 1, // 深度
							bevelEnabled: false // 对挤出的形状应用是否斜角
							// steps: 3
						};
						// ExtrudeGeometry: 沿着给定的路径拉伸一个平面图形 -两个参数= 路径和图形
						const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
						// 定义两种材质
						const material = new THREE.MeshBasicMaterial({
							// color: name === '成都市' ? 'red' : 'rgb(60,63,65)',
							color: 'rgb(60,63,65)',
							map: texture,
							transparent: true,
							opacity: 1
						});
						const material1 = new THREE.MeshBasicMaterial({
							// color: name === '成都市' ? 'red' : '#eddba7',
							color: '#eddba7',
							map: texture,
							transparent: true,
							opacity: 1
						});
						// 形成mesh网格
						const mesh = new THREE.Mesh(geometry, [material, material1]);
						// name === '成都市' ? mesh.position.z = 0.8: '';
						// 初始化透明度变化
						gsap.to([material, material1, lineMaterial, mesh.position], {
							opacity: 1,
							duration: 5,
							z: name === '成都市' ? 0.8 : 0,
							// repeat: -1, // 重复次数 无限循环-1
							// yoyo: true, // 往返运动
							// delay: 2, // 延迟时间
							ease: 'power1.inOut', // 动画类型
							onComplete: () => {
								// 动画完成回调
								console.log('动画完成');
							},
							onStart: () => {
								// 动画开始回调
								console.log('动画开始');
							}
						});

						const line = new THREE.Line(lineGeometry, lineMaterial);
						// 将省份的属性 加进来
						province.properties = elem.properties;
						province.name = elem.properties.name;

						province.add(mesh);
						province.add(line);
					});
				});
				map.add(province);
			});
			scene.add(map);
			console.log(map);
		},
		// 渲染地图边框
		initBorder() {
			const loader = new THREE.FileLoader();
			loader.load('json/border.json', jsondata => {
				// 四川边界
				const feature = JSON.parse(jsondata).features[0];
				const province = new THREE.Object3D();
				province.properties = feature.properties.name;
				// 点数据
				const coordinates = feature.geometry.coordinates;
				coordinates.forEach(coordinate => {
					// coordinate 多边形数据
					coordinate.forEach(rows => {
						const line = this.lineDraw(rows, 0xffffff);
						province.add(line);
					});
				});
				positions = new Float32Array(lines.flat(1));
				// 设置顶点
				geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
				// 设置 粒子透明度为 0
				opacitys = new Float32Array(positions.length).map(() => 0);
				geometry.setAttribute('aOpacity', new THREE.BufferAttribute(opacitys, 1));

				scene.add(province);
				// 控制 颜色和粒子大小
				const params = {
					pointSize: 4.0,
					pointColor: 'blue'
				};
				// 定义顶点着色器
				const vertexShader = `
          attribute float aOpacity;
          uniform float uSize;
          varying float vOpacity;
          void main(){
              gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
              gl_PointSize = uSize;
              vOpacity=aOpacity;
          }
          `;
				// 定义片元着色器

				const fragmentShader = `
          varying float vOpacity;
          uniform vec3 uColor;
          float invert(float n){
              return 1.-n;
          }
          void main(){
            if(vOpacity <=0.2){
                discard;
            }
            vec2 uv=vec2(gl_PointCoord.x,invert(gl_PointCoord.y));
            vec2 cUv=2.*uv-1.;
            vec4 color=vec4(1./length(cUv));
            color*=vOpacity;
            color.rgb*=uColor;

            gl_FragColor=color;
          }
          `;
				const material = new THREE.ShaderMaterial({
					vertexShader: vertexShader,
					fragmentShader: fragmentShader,
					transparent: true, // 设置透明
					uniforms: {
						uSize: {
							value: params.pointSize
						},
						uColor: {
							value: new THREE.Color(params.pointColor)
						}
					}
				});
				points = new THREE.Points(geometry, material);
				scene.add(points);
			});
		},
		//
		lineDraw(polygon, color) {
			const lineGeometry = new THREE.BufferGeometry();
			const pointsArray = [];
			polygon.forEach(row => {
				const [x, y] = projection(row);
				// 创建三维点
				pointsArray.push(new THREE.Vector3(x, -y, 0));
				if (indexBol) {
					lines.push([x, -y, 1.01]);
				}
			});
			indexBol = false;
			// 放入多个点
			lineGeometry.setFromPoints(pointsArray);
			const lineMaterial = new THREE.LineBasicMaterial({
				color: color
			});
			return new THREE.Line(lineGeometry, lineMaterial);
		},
		// 创建平面
		initPlane() {
			let textureLoader = new THREE.TextureLoader(); // 纹理加载器
			let texture = textureLoader.load(require('@/assets/img/6.png'));
			const geometry = new THREE.RingGeometry(10, 10.3, 30);
			const material = new THREE.MeshBasicMaterial({
				color: 0xffff00,
				map: texture,
				side: THREE.DoubleSide
			});
			const plane = new THREE.Mesh(geometry, material);
			plane.position.set(-2, -1.1, 0);
			scene.add(plane);
			this.animationPlane(plane);
		},
		// 平面旋转动画
		animationPlane(plane) {
			console.log(camera);
			let position = plane.rotation;
			// let animate1 =''
			gsap.to(position, {
				// x: 15,
				z: Math.PI * 2,
				// z: 20,
				duration: 5,
				repeat: -1, // 重复次数 无限循环-1
				yoyo: false, // 往返运动
				// delay: 2, // 延迟时间
				ease: 'none', // 动画类型
				onComplete: () => {
					// 动画完成回调
					console.log('动画完成');
				},
				onStart: () => {
					// 动画开始回调
					console.log('动画开始');
				}
			});
		},
		// 鼠标移入地图
		setRaycaster() {
			raycaster = new THREE.Raycaster();
			mouse = new THREE.Vector2();
			this.tooltip = this.$refs.tooltip;
			const onMouseMove = event => {
				mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
				mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
				this.tooltip.style.left = event.clientX + 2 + 'px';
				this.tooltip.style.top = event.clientY + 2 + 'px';
				// 计算物体和射线的焦点
				const intersects = raycaster.intersectObjects(
					scene.children, // 场景的
					true // 若为true，则同时也会检测所有物体的后代。否则将只会检测对象本身的相交部分
				);
				// 恢复上一次清空的
				if (this.lastPick) {
					this.lastPick.object.material[0].color.set('rgb(60,63,65)');
					this.lastPick.object.material[1].color.set('#eddba7');
				}
				this.lastPick = null;
				this.lastPick = intersects.find(
					item => item.object.material && item.object.material.length === 2
				);
				if (this.lastPick) {
					this.lastPick.object.material[0].color.set(0xff0000);
					this.lastPick.object.material[1].color.set(0xff0000);
				}
			};
			window.addEventListener('mousemove', onMouseMove, false);
		},
		// 模型加载
		initModel() {
			// const size = 30;
			// const divisions = 30;
			//
			// const gridHelper = new THREE.GridHelper( size, divisions );
			// gridHelper.position.set(0, -2.5, 0)
			// scene.add( gridHelper );
			// MeshBasicMaterial: 基本材质 MeshPhysicalMaterial: 高级材质
			// geometry = new THREE.BoxGeometry(5, 5, 5);
			// material = new THREE.MeshBasicMaterial({
			// 	color: 0xff0000,
			// 	material: 1, // 金属度
			// 	roughness: 0.5, // 粗超程度 - 可以形成漫反射
			// 	// clearcoat: 1, // 设置清漆，形成喷漆的效果
			// 	// clearcoatRoughness: 0,
			// 	sheen: 0.5
			// });
			// cube = new THREE.Mesh(geometry, material);
			// scene.add(cube);
		},
		// 设置动画
		animation() {
			// let obj = {
			//   ...camera.position,
			// }
			let position = camera.position;
			// let animate1 =''
			gsap.to(position, {
				// x: 15,
				y: -5,
				// z: 20,
				duration: 5,
				// repeat: -1, // 重复次数 无限循环-1
				// yoyo: true, // 往返运动
				// delay: 2, // 延迟时间
				ease: 'power1.inOut', // 动画类型
				onComplete: () => {
					// 动画完成回调
					console.log('动画完成');
				},
				onStart: () => {
					// 动画开始回调
					console.log('动画开始');
				}
			});
		},
		// 光线
		initLight() {
			// 环境光
			let light = new THREE.AmbientLight(0xffffff);
			scene.add(light);
			let directionalLight = new THREE.DirectionalLight('#fff', 1);
			// directionalLight.position.set(100, 50, 100);
			directionalLight.position.set(0, 0, 20);
			directionalLight.castShadow = true;
			directionalLight.shadow.camera.near = 10;
			directionalLight.shadow.camera.far = 200;
			directionalLight.shadow.mapSize.width = 1024;
			directionalLight.shadow.mapSize.height = 1024;
			directionalLight.shadow.camera.top = 6; // 显示位置大小
			directionalLight.shadow.camera.right = 6;
			directionalLight.shadow.camera.left = -20;
			directionalLight.shadow.camera.bottom = -20;
			directionalLight.shadow.radius = 3; // 调整阴影模糊程度，值越大越模糊
			scene.add(directionalLight);
			// 光源的坐标系
			// const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
			// scene.add(directionalLightCameraHelper);
			// 点光源
			// let point = new THREE.PointLight('#fff',0.5);
			// point.position.set(50, 5, 40); //点光源位置
			// point.castShadow = true;
			// scene.add(point); //点光源添加到场景中
			// const pmremGenerator = new THREE.PMREMGenerator(renderer); // 使用hdr作为背景色
			// pmremGenerator.compileEquirectangularShader();
			// const loader = new RGBELoader()
			// const scene = this.scene;
			// loader.setDataType(THREE.UnsignedByteType);
			// loader.load(require('../assets/hdr/u001.hdr'), (texture) => {
			//     console.log(texture)
			//     const envMap = pmremGenerator.fromEquirectangular(texture).texture;
			//     scene.environment = envMap; // 给场景添加环境光效果
			//     // scene.background = envMap; // 给场景添加背景图
			//     texture.dispose()
			//     pmremGenerator.dispose()
			//
			//   });
		},
		// 相机
		initScene() {
			// 相机设置
			let k = width / height; //窗口宽高比
			//创建相机对象（透视相机） - 相机可以缩放显示三维场景、对三维场景的不同角度进行取景显示。
			camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
			camera.position.set(0, -5, 10); //设置相机位置
			camera.lookAt(0, 0, 0); //设置相机方向(指向的场景对象)
		},
		// 渲染器
		initRenderer() {
			renderer = new THREE.WebGLRenderer({
				alpha: true,
				antialias: true
				// logarithmicDepthBuffer: true,
			});
			renderer.setSize(width, height); //设置渲染区域尺寸
			// renderer.setClearColor('#fff', 1); //设置背景颜色
			renderer.setClearAlpha(1); // 背景透明度
			renderer.render(scene, camera);
			renderer.clear(); // 此方法将颜色缓存初始化为当前颜色
			renderer.shadowMap.enabled = true; // 可以渲染阴影
			renderer.getPixelRatio(Math.min(window.devicePixelRatio, 2)); // 设置像素比
			renderer.outputEncoding = THREE.sRGBEncoding;
			this.$refs.map.appendChild(renderer.domElement);
			// document.body.append(state.domElement);
		},
		// hdr文件加载
		initHdr() {
			// 创建加载器
			let hrdLoader = new RGBELoader();
			hrdLoader.loadAsync('model/bg2.hdr').then(texture => {
				texture.encoding = THREE.sRGBEncoding;
				// texture.mapping = THREE.EquirectangularReflectionMapping;
				//将加载的材质texture设置给背景和环境
				scene.background = texture;
				// 给场景所有的物体添加默认的环境贴图
				scene.environment = texture;
			});
		},
		// 鼠标控制 - controls
		initControls() {
			// 控制器 放大倍数  缩小倍数  是否自动旋转及速度
			controls = new OrbitControls(camera, renderer.domElement);
			controls.target.set(0, 0, 0);
			// controls.autoRotate = true;
			controls.enableDamping = true; // 阻尼
			controls.dampingFactor = 1; // 阻尼系数大小控制
			controls.enablePan = true; // 是否开启右键拖拽功能
			controls.maxPolarAngle = Math.PI; //限制上下可视角度
			// controls.enableZoom = true // 是否可以缩放
			// controls.autoRotate = true // 是否自动旋转
			// controls.autoRotateSpeed = 0.3 // 旋转速度控制
			// controls.minDistance = 1; // 相机距离原点的最近距离
			// controls.maxDistance = 400; // 相机距离原点的最远距离
		},
		// 动画
		render() {
			// 获取时间 - 逐渐增加的时间
			// const elapsed = clock.getElapsedTime();
			// 通过摄像机和鼠标位置更新射线
			raycaster.setFromCamera(mouse, camera);
			// 动态更新点
			if (points && geometry.attributes.position) {
				currentPos += pointSpeed;
				for (let i = 0; i < pointSpeed; i++) {
					opacitys[(currentPos - i) % lines.length] = 0;
				}
				for (let i = 0; i < 200; i++) {
					opacitys[(currentPos + i) % lines.length] = i / 50 > 2 ? 2 : i / 50;
				}
				geometry.attributes.aOpacity.needsUpdate = true;
			}

			this.showTip();
			renderer.render(scene, camera); // 整体渲染
			controls.update();
			requestAnimationFrame(this.render); //请求再次执行渲染函数render
		},
		// 显示省份的信息
		showTip() {
			if (this.lastPick) {
				const properties = this.lastPick.object.parent.properties;
				this.tooltip.textContent = properties.name;
				this.tooltip.style.display = 'block';
			} else {
				this.tooltip.style.display = 'none';
			}
		}
	}
};
</script>

<style lang="scss" scoped>
.map-box {
	width: 100%;
	height: 100%;
	position: relative;
	.map {
		width: 100%;
		height: 100%;
	}
	.tooltip {
		position: absolute;
		z-index: 2;
		background: white;
		padding: 10px;
		border-radius: 2px;
		display: none;
	}
}
</style>
